import random,sys
class FractalMapGen:
    def __init__(self, sample, getCallback, setCallback, dimension = (64,64), step = 2):
        self.getTile = getCallback
        self.setTile = setCallback
        self.sample = sample
        self.step = step
        self.dimension = dimension
        self.citer = 1
        self.template = None
    
    def setTemplate(self,template):
        msx = len(template)
        step = self.step
        if msx % step:
            print("ERROR: template dimension should be divisible by step.")
            print("Template does not apply")
            return False
        self.template = template
        self.citer = int(msx/step)
        citer = self.citer
        sx,sy = self.dimension
        for x in range(citer * step):
            for y in range(citer * step):
                x0 = int(sx/(citer * step)) * x
                x1 = int(sx/(citer * step)) * (x + 1) - 1
                y0 = int(sy/(citer * step)) * y
                y1 = int(sy/(citer * step)) * (y + 1) - 1
                if template[x][y] == None:
                    csample = self.sample
                else:
                    csample = (template[x][y],)
                self.fill(x0,y0,x1,y1,random.sample(csample,1)[0])
        self.citer += 1
        return True
    
    def fill(self, x0, y0, x1, y1, val):
        for x in range(x0,x1+1):
            for y in range(y0,y1+1):
                self.setTile((x,y),val)
                
    def getSample(self, x0, y0, x1, y1):
        sx,sy = self.dimension
        left = x0 - 1 > 0
        right = x1 + 1 < sx
        up = y0 - 1 > 0 
        down = y1 + 1 < sy
        sample = []
        if left: sample.append(self.getTile((x0 - 1, y0)))
        if right: sample.append(self.getTile((x1 + 1, y1)))
        if up: sample.append(self.getTile((x0, y0 - 1)))
        if down: sample.append(self.getTile((x1, y1 + 1)))
        return sample
        
    def generate(self, showProgress = 1, maxIters = 0):
        random.seed(1)
        citer = self.citer
        step = self.step
        sx,sy = self.dimension
        if step < 2:
            print('ERROR: step should be > 1.')
            return False
        if sx % step or sy % step:
            print('ERROR: map dimensions should be divisible by step.')
            print('Generation aborted.')
            return False
        if maxIters > 0:
            minIters = float(min(float(sx)/step + 1, float(sy)/step + 1, maxIters - 1))
        elif maxIters < 0:
            minIters = float(min(float(sx)/step + maxIters, float(sy)/step + maxIters))
            if minIters < 1:
            # ~ if minIters < 0:
                print('ERROR: Incorrect maxIters value.')
                print('Generation aborted.')
                return False
        else:
            minIters = min(float(sx)/step + 1, float(sy)/step + 1)
        print(minIters)
        # 分析：step是固定值，越大，图像越碎，而citer在循环中逐渐增加
        while (sx/(citer * step) >= 1) and (sy/(citer * step) >= 1):
            # ~ hx = int(sx/(citer * step))
            # ~ hy = int(sy/(citer * step))
            for x in range(citer * step):
                for y in range(citer * step):
                    x0 = int(sx/(citer * step)) * x
                    x1 = int(sx/(citer * step)) * (x + 1) - 1
                    y0 = int(sy/(citer * step)) * y
                    y1 = int(sy/(citer * step)) * (y + 1) - 1
                    print(x, y, citer * step)
                    print(x0, y0, x1-x0, y1-y0)
                    if (citer > 1) or (self.template):
                        csample = self.getSample(x0,y0,x1,y1)
                    else:
                        csample = self.sample
                    self.fill(x0,y0,x1,y1,random.sample(csample,1)[0])
            citer += 1

            if showProgress == 1:
                sys.stdout.write('Progress %.1f' % (int(citer/(minIters) * 100)) + '%\r')
                sys.stdout.flush()
            elif showProgress == 2:
                percent = citer/(minIters)
                p0 = int(percent * 10)
                print('\033[0;0H')
                print('Progress |\033[41m%s\033[0m%s| %.1f' % (' ' * p0, ' ' * (10 - p0), int(percent * 100)) + '%')
                print('Iteration: %i' % citer)
            if maxIters and citer >= minIters:
                return True
        return True

from PIL import Image
sx,sy = 128,128
colors = ((255,0,0),(0,255,0),(0,0,255),(250,150,50))
image = Image.new("RGB", (sx,sy), (0,0,0,0))
fg = FractalMapGen(colors, image.getpixel, image.putpixel, (sx,sy), 4)
#fg = FractalMapGen(colors, image.getpixel, image.putpixel, (sx,sy), 16)
fg.generate(maxIters=4)
image.save("./test1.png", "PNG")
del image
